# simulator/echo_moni.py
"""
回波模拟器 - 通过接收雷达发射电磁波和场景参数，实时生成雷达回波
Echo Simulator - Receives radar transmit signals and scene parameters, generates real-time radar echoes
"""

import numpy as np
import pandas as pd
import os
from typing import Dict, List, Tuple, Any
from .radar_moni import TransmitSignalData

class EchoMoni:
    """回波模拟器 - 专注于生成雷达回波信号"""

    # 类级别的标志，用于控制天线方向图加载信息只打印一次
    _antenna_pattern_printed = False

    def __init__(self, radar_params: Dict):
        self.params = radar_params
        self.fs = self.params.get('sampling_rate', 100.0) * 1e6  # 采样频率，从MHz转换为Hz
        self.c = 3.0e8    # 光速

        # 天线方向图数据 - 三路：和、方位差、俯仰差
        self.sum_beam_pattern = None  # 和波束方向图
        self.azimuth_diff_pattern = None  # 方位差波束方向图
        self.elevation_diff_pattern = None  # 俯仰差波束方向图
        self.az_axis = None  # 方位角轴 (度)
        self.el_axis = None  # 俯仰角轴 (度)

        # 尝试加载天线方向图
        self._load_antenna_patterns()

    def _load_antenna_patterns(self):
        """加载天线方向图CSV文件 - 和、方位差、俯仰差三路"""
        # 获取config目录路径
        config_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config')
        sum_pattern_file = os.path.join(config_dir, 'sum_beam_pattern_linear.csv')
        az_diff_pattern_file = os.path.join(config_dir, 'azimuth_difference_pattern.csv')
        el_diff_pattern_file = os.path.join(config_dir, 'elevation_difference_pattern.csv')

        # 检查文件是否存在
        if not all([os.path.exists(f) for f in [sum_pattern_file, az_diff_pattern_file, el_diff_pattern_file]]):
            missing_files = [f for f in [sum_pattern_file, az_diff_pattern_file, el_diff_pattern_file] if not os.path.exists(f)]
            print(f"[EchoMoni] 警告：未找到以下天线方向图文件:")
            for f in missing_files:
                print(f"  - {f}")
            print("[EchoMoni] 将使用固定天线增益")
            return

        try:
            # 读取和通道方向图
            df_sum = pd.read_csv(sum_pattern_file, header=None)
            self.az_axis = df_sum.iloc[0, 1:].values.astype(float)
            self.el_axis = df_sum.iloc[1:, 0].values.astype(float)
            self.sum_beam_pattern = df_sum.iloc[1:, 1:].values.astype(float)

            # 读取方位差通道方向图
            df_az_diff = pd.read_csv(az_diff_pattern_file, header=None)
            self.azimuth_diff_pattern = df_az_diff.iloc[1:, 1:].values.astype(float)

            # 读取俯仰差通道方向图
            df_el_diff = pd.read_csv(el_diff_pattern_file, header=None)
            self.elevation_diff_pattern = df_el_diff.iloc[1:, 1:].values.astype(float)

            # 计算和通道的最大增益（dB值）
            max_sum_gain_linear = np.max(self.sum_beam_pattern)
            max_sum_gain_db = 10 * np.log10(max_sum_gain_linear) if max_sum_gain_linear > 0 else -np.inf

            # 只在第一次加载时打印
            if not EchoMoni._antenna_pattern_printed:
                print(f"[EchoMoni] 天线方向图已加载 (和、方位差、俯仰差):")
                print(f"  方位角范围: {self.az_axis[0]:.2f}° 到 {self.az_axis[-1]:.2f}°, 共{len(self.az_axis)}点")
                print(f"  俯仰角范围: {self.el_axis[0]:.2f}° 到 {self.el_axis[-1]:.2f}°, 共{len(self.el_axis)}点")
                print(f"  和通道方向图: {self.sum_beam_pattern.shape}, 最大增益: {max_sum_gain_db:.2f} dB (线性值: {max_sum_gain_linear:.4f})")
                print(f"  方位差通道方向图: {self.azimuth_diff_pattern.shape}, 最大值: {np.max(np.abs(self.azimuth_diff_pattern)):.4f}")
                print(f"  俯仰差通道方向图: {self.elevation_diff_pattern.shape}, 最大值: {np.max(np.abs(self.elevation_diff_pattern)):.4f}")
                EchoMoni._antenna_pattern_printed = True

        except Exception as e:
            print(f"[EchoMoni] 加载天线方向图失败: {e}")
            self.sum_beam_pattern = None
            self.azimuth_diff_pattern = None
            self.elevation_diff_pattern = None
            self.az_axis = None
            self.el_axis = None

    def _get_antenna_gain(self, azimuth: float, elevation: float) -> Tuple[float, float, float]:
        """
        根据目标方位角和俯仰角查询三路天线增益（线性值）

        Args:
            azimuth: 目标方位角 (度)
            elevation: 目标俯仰角 (度)

        Returns:
            (sum_gain, az_diff_gain, el_diff_gain): 和通道、方位差通道、俯仰差通道增益（线性值）
        """
        if self.sum_beam_pattern is None:
            # 如果没有加载方向图，使用固定增益
            fixed_gain = 10**(self.params.get('antenna_gain', 30.0)/10)
            return (fixed_gain, 0.0, 0.0)  # 差通道返回0

        # 检查目标是否在方向图覆盖范围内
        if (azimuth < self.az_axis[0] or azimuth > self.az_axis[-1] or
            elevation < self.el_axis[0] or elevation > self.el_axis[-1]):
            # 目标不在主瓣范围内，返回极小增益
            return (1e-6, 0.0, 0.0)  # 非常小的增益，相当于-60dB

        # 双线性插值查询增益
        # 找到最近的方位角索引
        az_idx = np.searchsorted(self.az_axis, azimuth)
        if az_idx == 0:
            az_idx = 1
        elif az_idx >= len(self.az_axis):
            az_idx = len(self.az_axis) - 1

        # 找到最近的俯仰角索引
        el_idx = np.searchsorted(self.el_axis, elevation)
        if el_idx == 0:
            el_idx = 1
        elif el_idx >= len(self.el_axis):
            el_idx = len(self.el_axis) - 1

        # 双线性插值
        az0, az1 = self.az_axis[az_idx-1], self.az_axis[az_idx]
        el0, el1 = self.el_axis[el_idx-1], self.el_axis[el_idx]

        # 插值权重
        t_az = (azimuth - az0) / (az1 - az0) if az1 != az0 else 0.5
        t_el = (elevation - el0) / (el1 - el0) if el1 != el0 else 0.5

        # 获取四个角的增益值 - 和通道
        g00_sum = self.sum_beam_pattern[el_idx-1, az_idx-1]
        g01_sum = self.sum_beam_pattern[el_idx-1, az_idx]
        g10_sum = self.sum_beam_pattern[el_idx, az_idx-1]
        g11_sum = self.sum_beam_pattern[el_idx, az_idx]

        # 双线性插值 - 和通道
        sum_gain = (g00_sum * (1 - t_az) * (1 - t_el) +
                    g01_sum * t_az * (1 - t_el) +
                    g10_sum * (1 - t_az) * t_el +
                    g11_sum * t_az * t_el)

        # 获取四个角的增益值 - 方位差通道
        g00_az = self.azimuth_diff_pattern[el_idx-1, az_idx-1]
        g01_az = self.azimuth_diff_pattern[el_idx-1, az_idx]
        g10_az = self.azimuth_diff_pattern[el_idx, az_idx-1]
        g11_az = self.azimuth_diff_pattern[el_idx, az_idx]

        # 双线性插值 - 方位差通道
        az_diff_gain = (g00_az * (1 - t_az) * (1 - t_el) +
                        g01_az * t_az * (1 - t_el) +
                        g10_az * (1 - t_az) * t_el +
                        g11_az * t_az * t_el)

        # 获取四个角的增益值 - 俯仰差通道
        g00_el = self.elevation_diff_pattern[el_idx-1, az_idx-1]
        g01_el = self.elevation_diff_pattern[el_idx-1, az_idx]
        g10_el = self.elevation_diff_pattern[el_idx, az_idx-1]
        g11_el = self.elevation_diff_pattern[el_idx, az_idx]

        # 双线性插值 - 俯仰差通道
        el_diff_gain = (g00_el * (1 - t_az) * (1 - t_el) +
                        g01_el * t_az * (1 - t_el) +
                        g10_el * (1 - t_az) * t_el +
                        g11_el * t_az * t_el)

        return (sum_gain, az_diff_gain, el_diff_gain)

    # 核心功能：生成回波信号（保留这个方法，因为这是回波模拟器的核心功能）

    def generate_echo_signal(self, tx_data: TransmitSignalData,
                           targets: List[Dict]) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
        """
        生成三路回波信号（和通道、方位差通道、俯仰差通道）

        Args:
            tx_data: 发射信号数据结构，包含：
                - iq_samples: IQ信号采样点
                - time_axis: 时间轴
                - pulse_width: 脉宽
                - rf_frequency: 射频频率
                - range_gate_start: 距离波门起始
                - range_gate_width: 距离波门宽度
                - sampling_rate: 采样率
                - bandwidth: 信号带宽
            targets: 目标状态列表，每个目标为字典格式，包含：
                - 'range': 距离 (m)
                - 'radial_velocity': 径向速度 (m/s)
                - 'rcs': 雷达截面积 (m²)
                - 'id': 目标ID
                - 'azimuth': 方位角 (度)
                - 'elevation': 俯仰角 (度)

        Returns:
            (t_total, sum_echo, az_diff_echo, el_diff_echo): 时间轴、和通道回波、方位差通道回波、俯仰差通道回波
        """

        # 从发射信号数据结构获取参数
        tx_signal = tx_data.iq_samples
        t_tx = tx_data.time_axis
        range_gate_start = tx_data.range_gate_start  # m
        range_gate_width = tx_data.range_gate_width  # m
        range_gate_end = range_gate_start + range_gate_width

        # 更新采样率（使用发射信号的采样率确保一致性）
        self.fs = tx_data.sampling_rate

        # 计算接收时间窗口
        gate_start_time = 2 * range_gate_start / self.c  # 双程延迟 (s)
        gate_end_time = 2 * range_gate_end / self.c  # 双程延迟 (s)
        gate_width_time = gate_end_time - gate_start_time

        # 接收信号的绝对时间：从仿真开始计算
        # 接收窗口开始时间 = 脉冲发射时刻 + 波门起始延迟
        rx_start_time = tx_data.pulse_start_time + gate_start_time

        # 接收信号采样数
        rx_samples = int(gate_width_time * self.fs)
        if rx_samples <= 0:
            # 波门设置无效，返回空信号
            t_total = t_tx.copy()
            zero_signal = np.zeros(len(t_total), dtype=complex)
            return t_total, zero_signal, zero_signal, zero_signal

        # 构建回波时间轴：使用仿真的绝对时间（从t=0开始）
        total_samples = int(gate_width_time * self.fs)
        # 时间轴从 rx_start_time 开始（包含脉冲时间戳和波门延迟）
        t_total = rx_start_time + np.arange(total_samples) / self.fs

        # 三路回波信号
        sum_echo = np.zeros(total_samples, dtype=complex)
        az_diff_echo = np.zeros(total_samples, dtype=complex)
        el_diff_echo = np.zeros(total_samples, dtype=complex)

        # 处理所有目标，但只有在距离波门内的目标会产生回波
        for target in targets:
            # 直接使用传入的目标状态（由 sim_engine 计算和更新）
            current_range_m = target['range']  # m
            rcs = target['rcs']
            azimuth = target.get('azimuth', 0.0)  # 目标方位角 (度)
            elevation = target.get('elevation', 0.0)  # 目标俯仰角 (度)

            # 检查目标是否在距离波门内
            if not (range_gate_start <= current_range_m <= range_gate_end):
                continue  # 目标不在波门内，跳过

            target_range = current_range_m  # 已经是米

            # 计算目标回波在接收时间窗口中的位置
            target_delay = 2 * target_range / self.c  # 双程延迟

            # 计算回波在接收时间轴中的相对位置
            relative_delay_in_rx = target_delay - gate_start_time  # 相对于波门开始的延迟

            # 修复：应该与gate_width_time比较，而不是gate_end_time
            if relative_delay_in_rx < 0 or relative_delay_in_rx > gate_width_time:
                continue  # 目标回波不在接收时间窗口内

            # 计算回波在总信号中的起始索引
            echo_start_idx = int(relative_delay_in_rx * self.fs)

            # 计算三路回波幅度（考虑雷达方程和天线方向图）
            sum_amp, az_diff_amp, el_diff_amp = self._calculate_echo_amplitude_from_range(
                target_range, rcs, tx_data, azimuth, elevation)

            # 获取并打印天线增益值
            sum_gain, az_diff_gain, el_diff_gain = self._get_antenna_gain(azimuth, elevation)
            sum_gain_db = 10 * np.log10(sum_gain) if sum_gain > 0 else -np.inf
            az_diff_gain_db = 10 * np.log10(abs(az_diff_gain)) if az_diff_gain != 0 else -np.inf
            el_diff_gain_db = 10 * np.log10(abs(el_diff_gain)) if el_diff_gain != 0 else -np.inf

            print(f"[PRT] 目标ID={target.get('id', 'unknown')}, 真实角度: 方位={azimuth:.2f}°, 俯仰={elevation:.2f}°")
            print(f"      天线增益: 和={sum_gain_db:.2f}dB, 方位差={az_diff_gain_db:.2f}dB, 俯仰差={el_diff_gain_db:.2f}dB")

            # 计算相位 - 使用发射信号的射频频率
            wavelength = self.c / tx_data.rf_frequency

            # 生成回波信号
            if echo_start_idx < len(t_total) - len(tx_signal):

                # 相位由目标距离决定
                # 双程路径相位：-4*π*R/λ
                phase_shift_raw = -4 * np.pi * target_range / wavelength

                # 折叠相位到[-π, π]范围，避免数值精度问题
                phase_shift = np.angle(np.exp(1j * phase_shift_raw))

                # 应用相位
                phase_factor = np.exp(1j * phase_shift)

                # 生成三路回波分量
                sum_component = sum_amp * tx_signal * phase_factor
                az_diff_component = az_diff_amp * tx_signal * phase_factor
                el_diff_component = el_diff_amp * tx_signal * phase_factor

                # 将回波信号添加到总信号中
                echo_end_idx = echo_start_idx + len(tx_signal)
                if echo_end_idx <= len(sum_echo):
                    sum_echo[echo_start_idx:echo_end_idx] += sum_component
                    az_diff_echo[echo_start_idx:echo_end_idx] += az_diff_component
                    el_diff_echo[echo_start_idx:echo_end_idx] += el_diff_component

        return t_total, sum_echo, az_diff_echo, el_diff_echo

    def _calculate_echo_amplitude_from_range(self, target_range: float, rcs: float,
                                           tx_data: TransmitSignalData,
                                           azimuth: float = 0.0, elevation: float = 0.0) -> Tuple[float, float, float]:
        """基于距离和RCS计算三路回波幅度（包含大气衰减和天线方向图）

        Args:
            target_range: 目标距离 (m)
            rcs: 雷达截面积 (m²)
            tx_data: 发射信号数据
            azimuth: 目标方位角 (度)
            elevation: 目标俯仰角 (度)

        Returns:
            (sum_amplitude, az_diff_amplitude, el_diff_amplitude): 三路回波电压幅度
        """
        # 雷达方程 - 使用发射信号数据中的参数
        pt = self.params.get('power', 1000.0)

        # 使用天线方向图增益（三路）
        sum_gain, az_diff_gain, el_diff_gain = self._get_antenna_gain(azimuth, elevation)
        wavelength = self.c / tx_data.rf_frequency

        # 计算大气衰减 - 双程路径
        frequency_ghz = tx_data.rf_frequency / 1e9
        atm_loss_db = self._get_atmospheric_attenuation(frequency_ghz)
        target_range_km = target_range / 1000  # 转换为km用于衰减计算
        total_atm_loss_db = atm_loss_db * target_range_km * 2  # 双程衰减
        atm_loss_factor = 10**(-total_atm_loss_db / 10)  # 转换为线性因子

        # 计算三路接收功率（包含大气衰减和天线方向图）
        # 雷达方程：Pr = (Pt * Gt² * λ² * σ * L_atm) / ((4π)³ * R⁴)
        denominator = (4 * np.pi)**3 * (target_range**4)

        # 和通道
        numerator_sum = pt * (sum_gain**2) * (wavelength**2) * rcs * atm_loss_factor
        pr_sum = numerator_sum / denominator
        sum_amplitude = np.sqrt(pr_sum)

        # 方位差通道（差通道增益可能为负值，取绝对值后开方）
        numerator_az_diff = pt * (az_diff_gain**2) * (wavelength**2) * rcs * atm_loss_factor
        pr_az_diff = numerator_az_diff / denominator
        az_diff_amplitude = np.sqrt(np.abs(pr_az_diff)) * np.sign(az_diff_gain)

        # 俯仰差通道（差通道增益可能为负值，取绝对值后开方）
        numerator_el_diff = pt * (el_diff_gain**2) * (wavelength**2) * rcs * atm_loss_factor
        pr_el_diff = numerator_el_diff / denominator
        el_diff_amplitude = np.sqrt(np.abs(pr_el_diff)) * np.sign(el_diff_gain)

        return sum_amplitude, az_diff_amplitude, el_diff_amplitude

    def _get_atmospheric_attenuation(self, frequency_ghz: float) -> float:
        """计算大气衰减因子 (dB/km)"""
        # 简化的大气衰减模型
        base_attenuation = 0.1
        humidity_factor = 1.5  # 假设中等湿度
        return base_attenuation * humidity_factor

    def update_radar_params(self, new_params: Dict):
        """更新雷达参数"""
        self.params.update(new_params)


# 保持向后兼容性：EchoProcessor 是 EchoMoni 的别名
EchoProcessor = EchoMoni